/**

This file is part of MaCI/GIMnet.

MaCI/GIMnet is free software: you can redistribute it and/or modify it 
under the terms of the GNU Lesser General Public License as published 
by the Free Software Foundation, either version 3 of the License, or 
(at your option) any later version.

MaCI/GIMnet is distributed in the hope that it will be useful, but WITHOUT 
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public 
License for more details.

You should have received a copy of the GNU Lesser General Public 
License along with GIMnet. (See COPYING.LESSER) If not, see 
<http://www.gnu.org/licenses/>.

**/
/**
 * $Id: gimutils.cpp,v 1.15 2009-05-13 07:18:25 amaula Exp $
 *
 * \file
 * \brief GIM utilities implementation file.
 * \author Harri Kukkonen
 * \author Antti Maula <antti.maula@tkk.fi>
 *
 */
#include "gimutils.h"
#include <assert.h>

namespace gim {

  time::time()
    : seconds (0),
      useconds (0)
  {
  }

  time::time(const long int sec,
             const long int usec)
    : seconds (sec),
      useconds (usec)
  {
    //  assert(useconds < 1000000L && useconds > -1000000L);
  }

  time::time(const time &right)
    : seconds (right.seconds),
      useconds (right.useconds)
  {
  }

  time::time(const int milliseconds)
    : seconds (milliseconds / 1000),
      useconds ((milliseconds * 1000) % 1000000)
  {
  }

  time::time(const unsigned int milliseconds)
    : seconds (milliseconds / 1000),
      useconds ((milliseconds * 1000) % 1000000)
  {
  }

  time::time(const bool settocurrent)
    : seconds (0),
      useconds (0)
  {
    if (settocurrent)
      setToCurrent();
  }
    
  time::time(const bool settocurrent,
             const int milliseconds)
    : seconds (0),
      useconds (0)
  {
    if (settocurrent)
      setToCurrentPlus(milliseconds);
  }

  time::~time()
  {

  }

  std::string time::toString(void) const
  {
    std::ostringstream vString;
    vString << seconds << "s " << useconds << "us";
    return vString.str(); 
  }
  
  std::string time::getDateString(const std::string &aStrftimeFormat) const
  {
    const time_t tvalue = seconds;
    const struct tm *t =  localtime( &tvalue );
    char datestr[128]; // All conversions should fit here, otherwise -> "<Not available>".
    std::string result = "<Not available>";

    if (t) {
      if (strftime(datestr, sizeof(datestr), aStrftimeFormat.c_str(), t)) {
        result = datestr;
      }
    }
  
    return result;
  }

  bool time::getLocalTime(struct tm &aTmStruct) const
  {
    bool result = true;
    const struct tm *t = localtime((const time_t *)&seconds);

    // Check whether call was succesfull.
    if (t) { 
      // Yes it was.
      aTmStruct = *t;

    } else {
      // No it was not.
      result = false;

    }

    return result;
  }

  void time::set(const long int sec,
                 const long int usec)
  {
    seconds = sec;
    useconds = usec;
  }
  /*
    void time::set(struct timeval t){
    seconds = t.tv_sec;
    useconds = t.tv_usec;
    }
  */

  bool time::setToCurrent()
  {
    if(ownTime_GetTimeOfDay(&seconds, &useconds) != ownStatusOK)
      {
        seconds = 0;
        useconds = 0;
        return false;
      }
    return true;
  }

  bool time::setToCurrentPlus(const int milliseconds)
  {
    if(ownTime_GetTimeOfDay(&seconds, &useconds) != ownStatusOK)
      {
        seconds = 0;
        useconds = 0;
        return false;
      }
    addTimeMs(milliseconds);
    return true;
  }

  bool time::setTimeMs(const int milliseconds)
  {
    seconds = milliseconds / 1000;
    useconds = (milliseconds * 1000) % 1000000;

    return true;
  }

  bool time::addTimeMs(const int milliseconds)
  {
    seconds += milliseconds / 1000;
    useconds += (milliseconds * 1000) % 1000000;

    return true;
  }

  bool time::reduceTimeMs(const int milliseconds)
  {
    seconds -= milliseconds / 1000;
    useconds -= (milliseconds * 1000) % 1000000;
  
    if (useconds < 0) {
      --seconds;
      useconds += 1000000;
    }
    
    return true;
  }

  int time::getTimeMs() const
  {
    int timems;
    const int maxms = INT_MAX / 1000;
    const int minms = INT_MIN / 1000;

    if (seconds >= maxms) {
      return INT_MAX;
    } else if (seconds <= minms) {
      return INT_MIN;
    }
  
    timems = 1000 * seconds;
    timems += useconds / 1000;
  
    return timems;
  }

  long long int time::getTimeUs() const {
    return (1000000 * seconds) + useconds;
  }

  unsigned int time::getAbsTimeMs(void) const
  {
    unsigned int timems = 0;

    if (seconds < 0) {
      timems = (seconds * -1000);
    } else {
      timems = (seconds * 1000);
    }

    if (useconds < 0) {
      timems += (useconds / -1000);
    } else {
      timems += (useconds / -1000);
    }
  
    return timems;
  }

  const time& time::operator= (const time& right)
  {
    seconds = right.seconds;
    useconds = right.useconds;
    return *this;
  }

  time& time::operator+= (const time& right)
  {
    *this = *this + right;
    return *this;
  }

  time& time::operator-= (const time& right)
  {
    *this = *this - right;
    return *this;
  }

  time time::operator+ (const time& right) const
  {
    time newtime(this->seconds, this->useconds);
    newtime.seconds += right.seconds;
    newtime.useconds += right.useconds;
    if (newtime.useconds > 999999) {
      newtime.useconds -= 1000000;
      ++(newtime.seconds);
    }
    //assert(newtime.useconds < 1000000L && newtime.useconds > -1000000L);
    return newtime;
  }

  time time::operator- (const time& right) const
  {
    time newtime(this->seconds, this->useconds);
    newtime.seconds -= right.seconds;
    if (newtime.useconds < right.useconds) {
      newtime.useconds += 1000000;
      --(newtime.seconds);
    }
    newtime.useconds -= right.useconds;
    //assert(newtime.useconds < 1000000L && newtime.useconds > -1000000L);
    return newtime;
  }

  bool time::operator== (const time& right) const
  {
    if (seconds == right.seconds && useconds == right.useconds) 
      return true;
    else
      return false;
  }

  bool time::operator< (const time& right) const
  {
    if (seconds < right.seconds)
      return true;
    if (seconds > right.seconds)
      return false;

    // Seconds must be equal, compare useconds.
    if (useconds < right.useconds)
      return true;

    return false;
  }

  bool time::operator> (const time& right) const
  {
    if (seconds > right.seconds)
      return true;
    if (seconds < right.seconds)
      return false;

    // Seconds must be equal, compare useconds.
    if (useconds > right.useconds)
      return true;

    return false;
  }

  void time::print(const char *prefix) const {
    if(prefix != NULL){
      fprintf(stderr,"time; '%s' %lds %ldus=%dms\n",prefix,seconds,useconds,getTimeMs());
    }
    else{
      fprintf(stderr,"time; %lds %ldus=%dms\n",seconds,useconds,getTimeMs());	
    }
  }

  version::version()
    : compVersion (0),
      majorVersion (0),
      minorVersion (0),
      extraVersion (0),
      build (0)
  {}
    
  version::version(int aVersion,
                   int aBuild)
    : compVersion (aVersion),
      majorVersion (aVersion / 10000),
      minorVersion ((aVersion % 10000) / 100),
      extraVersion (aVersion % 100),
      build (aBuild)
  {}
    
  version::version(int aMajorVersion,
                   int aMinorVersion,
                   int aExtraVersion,
                   int aBuild)
    : compVersion (1000*aMajorVersion + 100*aMinorVersion + aExtraVersion),
      majorVersion (aMajorVersion),
      minorVersion (aMinorVersion),
      extraVersion (aExtraVersion),
      build (aBuild)
  {}
    
  version::~version()
  {}

  bool version::setVersion(const int aVersion,
                           const int aBuild)
  {
    if (aVersion < 0 || aBuild < 0)
      return false;
  
    compVersion = aVersion;
    majorVersion = aVersion / 10000;
    minorVersion = (aVersion % 10000) / 100;
    extraVersion = aVersion % 100;
    build = aBuild;
    return true;
  }
    
  std::string version::getVersionString() const
  {
    std::ostringstream vString;
    vString << majorVersion << '.' << minorVersion << '.' << extraVersion <<
      " build " << build;
    return vString.str();
  }
}
